using System;
using System.Collections.Generic;
using System.Text;
using Chili.Opf3.Storages;
using Chili.Opf3.Mapping;

namespace Chili.Opf3.Query
{
	/// <summary>
	/// Represents a dynamic runtime object expression that is used to create dynamic storage commands.
	/// </summary>
	/// <remarks>
	/// This class represents a dynamic runtime object expression that converts a persistent object to a dynamic storage
	/// command. The data of the persistent object to create the command is queried when the <see cref="Expression.ToCompiledQuery">ToCompiledQuery</see>
	/// method is executed. All changes within the persistent object reflect immediately to the <see cref="CompiledQuery">CompiledQuery</see> output.
	/// </remarks>
	public sealed class PersistentObjectExpression : Expression
	{
		private object _obj;

		/// <summary>
		/// Creates a new instance of the <see cref="PersistentObjectExpression">PersistentObjectExpression</see> Class.
		/// </summary>
		/// <param name="queryType"> Specifies the <see cref="Opf3.Query.QueryTypes">QueryType</see> of the <see cref="PersistentObjectExpression">PersistentObjectExpression</see>.
		/// </param>
		/// <param name="obj">The object that is bound to the PersistentObjectExpression.</param>
		internal PersistentObjectExpression(QueryTypes queryType, object obj) : base(queryType)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");

			CheckFieldForValidData(obj);

			_obj = obj;
		}

		/// <summary>
		/// Checks the members of the persistent for valid data.
		/// </summary>
		/// <param name="obj">The persistent object whose members are checked.</param>
		private void CheckFieldForValidData(object obj)
		{
			TypeMapping mapping = MappingContainer.GetTypeMapping(obj);
			foreach(IMemberInfo member in mapping.Members)
			{
				// check if the field allows null values.
				if (!member.FieldAttribute.AllowDBNull)
				{
					// if not check the value of the field.
					if (member.GetValue(obj) == null)
						throw new ConstraintException(string.Format("Member {0} is null. Not allowed.", member.Name));
				}

				// check if the member type is a string.
				if (member.MemberType == typeof(string))
				{
					// get the value for it.
					string value = (string)member.GetValue(obj);
					// if null do nothing.
					if (value != null)
					{
						// compare the sizes of the string. if too long throw an exception.
						if (value.Length > member.FieldAttribute.MaxLength)
						{
							throw new ConstraintException(
								string.Format("Member content is to long: {0} (only {1} allowed)", value.Length,
								member.FieldAttribute.MaxLength));
						}
					}
				}
			}
		}

		/// <summary>
		/// Returns the object managed by the expression.
		/// </summary>
		public object Persistent
		{
			get { return _obj; }
		}
	}
}
